import VNode, { cloneVNode } from './vnode'
import { createElement } from './create-element'
import { resolveInject } from '../instance/inject'
import { normalizeChildren } from '../vdom/helpers/normalize-children'
import { resolveSlots } from '../instance/render-helpers/resolve-slots'
import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
import { installRenderHelpers } from '../instance/render-helpers/index'

import {
  isDef,
  isTrue,
  hasOwn,
  isArray,
  camelize,
  emptyObject,
  validateProp
} from '../util/index'
import type { Component } from 'types/component'
import type { VNodeData } from 'types/vnode'
// 关于Vue.js框架中函数式组件渲染上下文（FunctionalRenderContext）和创建函数式组件（createFunctionalComponent）的实现
export function FunctionalRenderContext(
  data: VNodeData,  // 传入组件的数据对象
  props: Object,    // 组件的属性对象
  children: Array<VNode> | undefined,  // 子虚拟节点数组，可能为undefined
  parent: Component,  // 父组件实例
  Ctor: typeof Component  // 组件构造器
) {
  const options = Ctor.options // 获取组件构造器的选项
  // ensure the createElement function in functional components
  // gets a unique context - this is necessary for correct named slot check
  // 确保函数式组件中的createElement函数有一个唯一的上下文，这对于正确的具名插槽检查是必要的
  let contextVm
  if (hasOwn(parent, '_uid')) {  // 如果父组件有_uid属性（即它是一个Vue实例）
    contextVm = Object.create(parent)  // 创建一个父组件实例的原型继承对象
    contextVm._original = parent  // 保存对原始父组件实例的引用
  } else {  // 如果传入的上下文也是一个函数式上下文
    contextVm = parent
    // @ts-ignore
    parent = parent._original  // 获取真正的父组件实例
  }
  const isCompiled = isTrue(options._compiled)  // 判断组件是否已编译
  const needNormalization = !isCompiled  // 如果未编译，则需要规范化
// 初始化渲染上下文对象的属性
  this.data = data
  this.props = props
  this.children = children
  this.parent = parent
  this.listeners = data.on || emptyObject  // 事件监听器
  this.injections = resolveInject(options.inject, parent)  // 注入的依赖
  this.slots = () => {  // 定义获取插槽的方法
    if (!this.$slots) {
      normalizeScopedSlots(
        parent,
        data.scopedSlots,
        (this.$slots = resolveSlots(children, parent))
      )
    }
    return this.$slots
  }
 // 为scopedSlots属性定义getter，以便按需规范化
 Object.defineProperty(this, 'scopedSlots', {
    enumerable: true,
    get() {
      return normalizeScopedSlots(parent, data.scopedSlots, this.slots())
    }
  } as any)

  // support for compiled functional template
   // 支持编译过的函数式模板  
  if (isCompiled) {
    this.$options = options  // 暴露$options供renderStatic()使用
    this.$slots = this.slots()  // 预先解析插槽
    this.$scopedSlots = normalizeScopedSlots(
      parent,
      data.scopedSlots,
      this.$slots
    )
  }
// 如果定义了作用域ID，则自定义_c方法以附加额外的信息到虚拟节点上
  if (options._scopeId) {
    this._c = (a, b, c, d) => {
      const vnode = createElement(contextVm, a, b, c, d, needNormalization) // 默认的createElement方法
      if (vnode && !isArray(vnode)) {
        vnode.fnScopeId = options._scopeId
        vnode.fnContext = parent
      }
      return vnode
    }
  } else {
    this._c = (a, b, c, d) =>
      createElement(contextVm, a, b, c, d, needNormalization)
  }
}
// 安装渲染辅助方法到FunctionalRenderContext的原型上
installRenderHelpers(FunctionalRenderContext.prototype)

export function createFunctionalComponent(
  Ctor: typeof Component,  // 组件构造器
  propsData: Object | undefined,  // 传入组件的属性数据
  data: VNodeData,  // 传入组件的数据对象
  contextVm: Component,  // 上下文Vue实例
  children?: Array<VNode>  // 子虚拟节点数组
): VNode | Array<VNode> | void {
  const options = Ctor.options  // 获取组件构造器的选项
  const props = {}  // 初始化props对象
  const propOptions = options.props  // 获取props选项
  if (isDef(propOptions)) {  // 如果定义了props选项
    for (const key in propOptions) {  // 遍历props选项
      props[key] = validateProp(key, propOptions, propsData || emptyObject)  // 验证并设置prop
    }
  } else {  // 如果没有定义props选项
    if (isDef(data.attrs)) mergeProps(props, data.attrs)  // 合并attrs到props
    if (isDef(data.props)) mergeProps(props, data.props)  // 合并props到props
  }
// 创建渲染上下文实例
  const renderContext = new FunctionalRenderContext(
    data,
    props,
    children,
    contextVm,
    Ctor
  )
// 调用组件的render函数渲染组件
  const vnode = options.render.call(null, renderContext._c, renderContext)
 // 处理渲染结果
 if (vnode instanceof VNode) {  // 如果渲染结果是单个虚拟节点
    return cloneAndMarkFunctionalResult(
      vnode,
      data,
      renderContext.parent,
      options,
      renderContext
    )
  } else if (isArray(vnode)) {  // 如果渲染结果是虚拟节点数组
    const vnodes = normalizeChildren(vnode) || []  // 规范化子节点
    const res = new Array(vnodes.length)  // 创建一个结果数组
    for (let i = 0; i < vnodes.length; i++) {  // 遍历子节点
      res[i] = cloneAndMarkFunctionalResult(
        vnodes[i],
        data,
        renderContext.parent,
        options,
        renderContext
      )
    }
    return res
  }
}
// 克隆并标记函数式组件的渲染结果
function cloneAndMarkFunctionalResult(
  vnode,  // 虚拟节点
  data,  // 数据对象
  contextVm,  // 上下文Vue实例
  options,  // 组件选项
  renderContext  // 渲染上下文
) {
  // #7817 clone node before setting fnContext, otherwise if the node is reused
  // (e.g. it was from a cached normal slot) the fnContext causes named slots
  // that should not be matched to match.
  const clone = cloneVNode(vnode)  // 克隆虚拟节点
  clone.fnContext = contextVm  // 设置上下文Vue实例
  clone.fnOptions = options  // 设置组件选项
  if (__DEV__) {  // 如果是开发模式
    ;(clone.devtoolsMeta = clone.devtoolsMeta || ({} as any)).renderContext =
      renderContext  // 设置渲染上下文信息以便调试
  }
  if (data.slot) {  // 如果指定了插槽名
    ;(clone.data || (clone.data = {})).slot = data.slot  // 设置插槽名
  }
  return clone  // 返回克隆后的虚拟节点
}

// 合并属性对象
function mergeProps(to, from) {
  for (const key in from) {  // 遍历源对象
    to[camelize(key)] = from[key]  // 将属性名转换为驼峰式并合并到目标对象
  }
}
